﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

namespace ICU
{
    class Program
    {
        static Server server;
        static Dictionary<string, string> helpPage = new Dictionary<string, string>
        {
            {"help","显示帮助 Usage: help [命令|-命令的一部分]" },
            {"stop","关闭服务器" },
            {"ban","封禁, Usage: ban <id:number>" },
            {"unban","取消封禁, Usage: unban <id:number>" },
            {"list","列出服务器信息(json) Usage: list <user|group>" },
            {"save","强制保存服务端数据"},
            {"restart","重启服务器"},
            {"adduser","添加用户 Usage: adduser <id> <username> <password>" },
            {"addgroup","添加群 Usage: addgroup <id> <owner> <name>" },
            {"mailto","向某个用户发送邮件 Usage: mailto <id> <message>" },
            {"message", "查看某个消息" },
            {"plugin", "管理服务器插件" },
            {"fuck", "Oh f***! What happened just now? Maybe......something amazing?" },
        };
        static int SearchHelp(string target, out List<string> result)
        {
            int found = 0;
            result = new List<string>();
            foreach (var i in helpPage)
            {
                if (i.Key.StartsWith(target))
                {
                    found += 1;
                    result.Add(i.Key);
                }
            }
            return found;
        }
        static void Main(string[] args)
        {
            Console.CancelKeyPress += Console_CancelKeyPress;
            server = new Server();
            IServer.Logger logger = new IServer.Logger("Input");
            IServer.Logger cmdLogger = new IServer.Logger("Cmooand Line Output");
#if DEBUG
            server.Test();
#endif
            // Set up
            for (int i = 0; i < args.Length; i++)
            {
                switch (args[i])
                {
                    case "-help":
                        Console.WriteLine("Help - ICU Command Line");
                        Console.WriteLine("  -load-plugin: Load a plugin before the server starts.");
                        Console.WriteLine("  -set-work-directory: Set working directory.");
                        Console.WriteLine("  ");
                        Console.WriteLine("  ");
                        Console.WriteLine("  ");
                        Console.WriteLine("  ");
                        return;
                    case "-load-plugin":
                        i++;
                        server.LoadPluginByFullName(args[i]);
                        logger.Info($"before_start: -load-plugin {args[i]}");
                        break;
                    case "-set-work-directory":
                        i++;
                        Environment.CurrentDirectory = args[i];
                        break;
                    default:
                        break;
                }
            }
            SetupDone:
            // Setup Done
            Console.WriteLine("Starting server...");
            server.Start();
            while (true)
            {
                try
                {
                    Console.Write('>');
                    dynamic cmd = Console.ReadLine();
                    logger.Info(cmd);
                    cmd = cmd.Split(' ');
                    if (cmd.Length == 0)
                    {
                        continue;
                    }
                Begin:;
                    switch (cmd[0])
                    {
                        case "":
                            break;
                        case "fuck":
                            Console.WriteLine(helpPage["fuck"]);
                            break;
#if DEBUG
                        case "debug":
                            if (cmd.Length > 1)
                            {
                                switch (cmd[1])
                                {
                                    case "help":
                                        Console.WriteLine("Debug mode help:");
                                        Console.WriteLine("  null-check: check is there null in ICU.Server instance.");
                                        Console.WriteLine("  this-value: Display the value of server as JSON.");
                                        Console.WriteLine("");
                                        Console.WriteLine("");
                                        Console.WriteLine("");
                                        Console.WriteLine("");
                                        break;
                                    case "null-check":
                                        server.NullCheck();
                                        break;
                                    case "this-value":
                                        foreach (var item in server.GetType().GetRuntimeFields())
                                        {
                                            try
                                            {
                                                Console.WriteLine(item.Name + ": " + JsonConvert.SerializeObject(item.GetValue(server)));
                                            }
                                            catch { }
                                        }
                                        foreach (var item in server.GetType().GetRuntimeProperties())
                                        {
                                            try
                                            {
                                                Console.WriteLine(item.Name + ": " + JsonConvert.SerializeObject(item.GetValue(server)));
                                            }
                                            catch { }
                                        }
                                        break;
                                    default:
                                        Console.WriteLine("Invaild args.");
                                        break;
                                }
                            }
                            else
                            {
                                Console.WriteLine("Invaild args.");
                            }
                            break;
#endif
                        case "addgroup":
                            if (cmd.Length < 4) break;
                            server.Groups.Add(Convert.ToInt64(cmd[1]), new Group(Convert.ToInt64(cmd[2]), Convert.ToInt64(cmd[1]), cmd[3]));
                            break;
                        case "message":
                            if (cmd.Length < 2)
                            {
                                Console.WriteLine("Invalid args.");
                                break;
                            }
                            Console.WriteLine(JsonConvert.SerializeObject(server.GetMessage(Convert.ToInt64(cmd[1]), false)));
                            
                            break;
                        case "list":
                            if (cmd.Length < 2)
                            {
                                Console.WriteLine("Invaild args.");
                                break;
                            }
                            if (cmd[1] == "user")
                            {
                                Console.WriteLine(JsonConvert.SerializeObject(server.Users, Formatting.Indented));
                                break;
                            }
                            else if (cmd[1] == "group")
                            {
                                Console.WriteLine(JsonConvert.SerializeObject(server.Groups, Formatting.Indented));
                                break;
                            }
                            else
                            {
                                Console.WriteLine("Invaild args.");
                                break;
                            }
                        case "adduser":
                            if (cmd.Length < 4) break;
                            server.Users.Add(Convert.ToInt64(cmd[1]), new User { id = Convert.ToInt64(cmd[1]), username = cmd[2], password = Server.SHA256(cmd[3]) });
                            break;
                        case "stop":
                            server.Stop();
                            Console.WriteLine("The server has been closed, press any key to continue.");
                            _ = Console.ReadKey();
                            return;
                        case "restart":
                            server.Stop();
                            Console.WriteLine("The server will restart in 10 seconds, please wait.");
                            Task.Delay(1000).Wait();
                            string cmdline = "-args \"";
                            foreach (var item in args)
                            {
                                cmdline += $"\\\"{item}\\\" ";
                            }
                            cmdline += "\" ";
                            foreach (var item in server.installTask)
                            {
                                cmdline += $"-copy \"{item.Key}\" \"{item.Value}\" ";
                            }
                            try
                            {
                                System.Diagnostics.Process.Start(Path.Combine(AppDomain.CurrentDomain.BaseDirectory ?? "", "Updater.exe"), $"-delay 9000 {cmdline}-start");
                            }
                            catch (System.ComponentModel.Win32Exception e)
                            {
                                Console.WriteLine("Error: Updater.exe is not exist or unable. please use stop command.\n{0}", e.Message);
                                break;
                            }
                            return;
                        case "mailto":
                            if (cmd.Length < 3) break;
                            server.SendEmail(server.Users[Convert.ToInt64(cmd[1])].email, "An email from ICU server console.", cmd[2]);
                            break;
                        case "save":
                            if (cmd.Length > 1)
                            {
                                if (cmd[1] == "config")
                                {
                                    server.SaveConfig();
                                }
                            }
                            else
                            {
                                server.Save();
                            }
                            break;
                        case "ban":
                            if (cmd.Length > 1)
                            {
                                server.Ban(Convert.ToInt64(cmd[1]));
                            }
                            else
                            {
                                Console.WriteLine("Invaild args.");
                                break;
                            }
                            break;
                        case "unban":
                            if (cmd.Length > 1)
                            {
                                server.UnBan(Convert.ToInt64(cmd[1]));
                            }
                            else
                            {
                                Console.WriteLine("Invaild args.");
                                break;
                            }
                            break;
                        case "?":
                        case "help":
                        Help:;
                            if (cmd.Length == 1)
                            {
                                Console.WriteLine("Help for ICU server, total:{0}", helpPage.Count);
                                foreach (var i in helpPage)
                                {
                                    Console.Write(i.Key + ":");
                                    Console.CursorLeft = 15;
                                    Console.WriteLine(i.Value);
                                }
                            }
                            else
                            {
                                if (cmd[1][0] == '-')
                                {
                                    if (cmd[1] == "-")
                                    {
                                        cmd = new string[1] { "help" };
                                        goto Begin;
                                    }
                                    {
                                        int found = SearchHelp(cmd[1][1..], out List<string> r);
                                        foreach (var i in r)
                                        {
                                            Console.WriteLine(i + ": " + helpPage[i]);
                                        }
                                        Console.WriteLine("help: Searching finisheed. Total: " + found);
                                    }
                                }
                                else
                                {
                                    if (helpPage.TryGetValue(cmd[1], out string result))
                                    {
                                        Console.WriteLine("{0}: {1}", cmd[1], result);
                                    }
                                    else
                                    {
                                        Console.WriteLine("help: Unknown command, try 'help -" + cmd[1] + "'?");
                                    }
                                }
                            }
                            break;
                        case "plugin":
                            if (cmd.Length > 1)
                            {
                                switch (cmd[1])
                                {
                                    case "list":
                                        Console.Write("Plugins: ");
                                        foreach (var item in server.Plugins)
                                        {
                                            Console.Write($"'{item.Key}', ");
                                        }
                                        Console.WriteLine();
                                        break;
                                    case "list-all":
                                        Console.WriteLine(new WebClient().DownloadString("http://www.starlight.cool:13123/api/icu/plugins"));
                                        break;
                                    case "help":
                                        Console.WriteLine("Help for plugin:");
                                        Console.WriteLine();
                                        Console.WriteLine("  list: 查看已安装的插件");
                                        Console.WriteLine("  list-all: 联机查找所有插件");
                                        Console.WriteLine("  update: 联机升级插件 TODO");
                                        Console.WriteLine("  download: 联机下载插件 TODO");
                                        Console.WriteLine("  reload: 重载插件");
                                        Console.WriteLine();
                                        break;
                                    case "reload":
                                        Console.WriteLine("Please notice that reload subcommand will NOT load the newest dll from disk. If you want, please use restart.");
                                        if (cmd.Length <= 2)
                                        {
                                            server.ReloadPlugin();
                                        }
                                        else
                                        {
                                            server.ReloadPlugin(cmd[2]);
                                        }
                                        break;
                                    case "download":
                                        string url = cmd[2];
                                        if (!url.StartsWith("http"))
                                        {
                                            url = "http://www.starlight.cool:13123/api/icu/plugins/u/" + url;
                                        }
                                        foreach (var item in server.Plugins)
                                        {
                                            if (item.Value.UpdateUrl == url)
                                            {
                                                cmdLogger.Error("The Plugin has already installed");
                                                cmd[0] = "";
                                                goto Begin;
                                            }
                                        }
                                        try
                                        {
                                            WebClient client = new WebClient();
                                            cmdLogger.Info($"Plugin Version: {client.DownloadString($"{url}?type=check")}, Downloading...");
                                            client.DownloadFile($"{url}?type=download", "D_NP");
                                            server.installTask.Add(Path.Combine("plugins", url.Substring(url.LastIndexOf("/") + 1)), "D_NF");
                                            cmd[0] = "restart";
                                            goto Begin;
                                        }
                                        catch (Exception ex)
                                        {
                                            Console.WriteLine(ex);
                                        }
                                        break;
                                    default:
                                        Console.WriteLine("plugin: Unknown command, try 'plugin help'?");
                                        break;
                                }
                            }
                            else
                            {
                                Console.WriteLine("plugin: Unknown command, try 'plugin help'?");
                            }
                            break;
                        default:
                            {
                                int found = SearchHelp(cmd[0], out List<string> r);
                                if (r.Count == 1)
                                {
                                    Console.WriteLine("Unknown command. Do you mean '" + r[0] + "'?");
                                }
                                else
                                {
                                    Console.WriteLine("Unknown command, try 'help -" + cmd[0] + "'?");
                                }
                                Console.Write(">");
                                string input = Console.ReadLine();
                                logger.Info(input);
                                if (input == "fuck")
                                {
                                    if (found == 1)
                                    {
                                        cmd[0] = r[0];
                                        goto Begin;
                                    }
                                    else
                                    {
                                        string[] ncmd = new string[2];
                                        ncmd[0] = "help";
                                        ncmd[1] = "-" + cmd[0];
                                        cmd = ncmd;
                                        goto Help;
                                    }
                                }
                                else
                                {
                                    cmd = input.Split(' ');
                                    goto Begin;
                                }
                            }
                    }
                }
                catch (Exception e)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine(e);
                    Console.ForegroundColor = ConsoleColor.Gray;
                }
            }
        }

        private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
        {
            server.Stop();
            IServer.Logger.Exit();
        }
    }
}
